home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 May: Tool Chest / Developer CD Series May 1996 (Tool Chest) (Apple Computer) (1996).iso / Tool Chest / Text / WASTE / WASTE 1.1.2 Distribution / WASTE Source / WEDrawing.p < prev    next >
Encoding:
Text File  |  1995-10-12  |  32.6 KB  |  1,085 lines  |  [TEXT/CWIE]

  1. unit WEDrawing;
  2.  
  3. { WASTE PROJECT: }
  4. { Drawing routines and other basic support functions }
  5.  
  6. { Copyright © 1993-1995 Marco Piovanelli }
  7. { All Rights Reserved }
  8.  
  9. interface
  10.     uses
  11.         WEObjects;
  12.  
  13.     function WEOffsetToLine (offset: LongInt;
  14.                                     hWE: WEHandle): LongInt;
  15.     function _WEPixelToLine (vOffset: LongInt;
  16.                                     hWE: WEHandle): LongInt;
  17.     function _WEOffsetToRun (offset: LongInt;
  18.                                     hWE: WEHandle): LongInt;
  19.     procedure _WEGetIndStyle (runIndex: LongInt;
  20.                                     var info: WERunInfo;
  21.                                     hWE: WEHandle);
  22.     procedure WEGetRunInfo (offset: LongInt;
  23.                                     var info: WERunInfo;
  24.                                     hWE: WEHandle);
  25.     function WEGetSelectedObject (var hObjectDesc: WEObjectDescHandle;
  26.                                     hWE: WEHandle): OSErr;
  27.     function WEFindNextObject (offset: LongInt;
  28.                                     var hObjectDesc: WEObjectDescHandle;
  29.                                     hWE: WEHandle): LongInt;
  30.     procedure _WEContinuousStyleRange (rangeStart, rangeEnd: LongInt;
  31.                                     var mode: Integer;
  32.                                     var ts: WETextStyle;
  33.                                     hWE: WEHandle);
  34.     procedure _WESynchNullStyle (hWE: WEHandle);
  35.     function WEContinuousStyle (var mode: Integer;
  36.                                     var ts: WETextStyle;
  37.                                     hWE: WEHandle): Boolean;
  38.     procedure _WESegmentLoop (firstLine, lastLine: LongInt;
  39.                                     function Callback (pLine: LinePtr;
  40.                                                                     pAttrs: WERunAttributesPtr;
  41.                                                                     pSegment: Ptr;
  42.                                                                     segmentStart, segmentLength: LongInt;
  43.                                                                     styleRunPosition: JustStyleCode): Boolean;
  44.                                     hWE: WEHandle);
  45.     procedure _WEDrawLines (firstLine, lastLine: LongInt;
  46.                                     doErase: Boolean;
  47.                                     hWE: WEHandle);
  48.     function _WECalcPenIndent (slop: Integer;
  49.                                     alignment: Integer): Integer;
  50.     procedure _WESaveQDEnvironment (port: GrafPtr;
  51.                                     saveColor: Boolean;
  52.                                     var theEnvironment: QDEnvironment);
  53.     procedure _WERestoreQDEnvironment (var theEnvironment: QDEnvironment);
  54.     procedure _WEFillFontInfo (port: GrafPtr;
  55.                                     var targetStyle: WERunAttributes);
  56.     procedure _WECopyStyle (var sourceStyle, targetStyle: WETextStyle;
  57.                                     offStyles: Integer;
  58.                                     mode: Integer);
  59.     function _WEOffsetInRange (offset: LongInt;
  60.                                     edge: SignedByte;
  61.                                     rangeStart, rangeEnd: LongInt): Boolean;
  62.  
  63. implementation
  64.     uses
  65.         Palettes, QDOffscreen, ToolUtils;
  66.  
  67. { If WASTE_RESOLVE_FONT_DESIGNATORS is TRUE, font designators (the special }
  68. { IDs 0 and 1 that identify the system and the application font, respectively) }
  69. { are mapped by _WECopyStyle to the actual font IDs }
  70.  
  71. {$SETC WASTE_RESOLVE_FONT_DESIGNATORS = TRUE}
  72.  
  73.     function WEOffsetToLine (offset: LongInt;
  74.                                     hWE: WEHandle): LongInt;
  75.  
  76. { given a byte offset into the text, find the corresponding line index }
  77.  
  78.         var
  79.             pWE: WEPtr;
  80.             pLines: LineArrayPtr;
  81.             minIndex, maxIndex, index: LongInt;
  82.     begin
  83.         pWE := hWE^;
  84.  
  85. { get a pointer to the line array }
  86.         pLines := pWE^.hLines^;
  87.  
  88. { do a fast binary search through the style run array }
  89.         minIndex := 0;
  90.         maxIndex := pWE^.nLines;
  91.  
  92.         while (minIndex < maxIndex) do
  93.             begin
  94.                 index := BSR(minIndex + maxIndex, 1);
  95.                 if (offset >= pLines^[index].lineStart) then
  96.                     if (offset < pLines^[index + 1].lineStart) then
  97.                         Leave
  98.                     else
  99.                         minIndex := index + 1
  100.                 else
  101.                     maxIndex := index;
  102.             end;  { while }
  103.  
  104.         WEOffsetToLine := index;
  105.  
  106.     end;  { WEOffsetToLine }
  107.  
  108.     function _WEPixelToLine (vOffset: LongInt;
  109.                                     hWE: WEHandle): LongInt;
  110.  
  111. { given a vertical pixel offset in local coordinates, }
  112. { find the corresponding line index }
  113.  
  114.         var
  115.             pWE: WEPtr;
  116.             pLines: LineArrayPtr;
  117.             minIndex, maxIndex, index: LongInt;
  118.     begin
  119.         pWE := hWE^;
  120.  
  121. { get a pointer to the line array }
  122.         pLines := pWE^.hLines^;
  123.  
  124. { do a fast binary search through the style run array }
  125.         minIndex := 0;
  126.         maxIndex := pWE^.nLines;
  127.  
  128.         while (minIndex < maxIndex) do
  129.             begin
  130.                 index := BSR(minIndex + maxIndex, 1);
  131.                 if (vOffset >= pLines^[index].lineOrigin) then
  132.                     if (vOffset < pLines^[index + 1].lineOrigin) then
  133.                         Leave
  134.                     else
  135.                         minIndex := index + 1
  136.                 else
  137.                     maxIndex := index;
  138.             end;  { while }
  139.  
  140.         _WEPixelToLine := index;
  141.  
  142.     end;  { _WEPixelToLine }
  143.  
  144.     function _WEOffsetToRun (offset: LongInt;
  145.                                     hWE: WEHandle): LongInt;
  146.         var
  147.             pWE: WEPtr;
  148.             pRuns: RunArrayPtr;
  149.             minIndex, maxIndex, index: LongInt;
  150.     begin
  151.         _WEOffsetToRun := 0;
  152.         pWE := hWE^;
  153.  
  154. { get a pointer to the style run array }
  155.         pRuns := pWE^.hRuns^;
  156.  
  157. { do a fast binary search through the style run array }
  158.         minIndex := 0;
  159.         maxIndex := pWE^.nRuns;
  160.  
  161.         while (minIndex < maxIndex) do
  162.             begin
  163.                 index := BSR(minIndex + maxIndex, 1);
  164.                 if (offset >= pRuns^[index].runStart) then
  165.                     if (offset < pRuns^[index + 1].runStart) then
  166.                         Leave
  167.                     else
  168.                         minIndex := index + 1
  169.                 else
  170.                     maxIndex := index;
  171.             end;  { while }
  172.  
  173.         _WEOffsetToRun := index;
  174.  
  175.     end;  { _WEOffsetToRun }
  176.  
  177.     procedure _WEGetIndStyle (runIndex: LongInt;
  178.                                     var info: WERunInfo;
  179.                                     hWE: WEHandle);
  180.         var
  181.             pWE: WEPtr;
  182.             pTheRun: RunArrayPeek;
  183.     begin
  184.         pWE := hWE^;
  185.  
  186. { get a pointer to the specified run array element }
  187.         pTheRun := @pWE^.hRuns^^[runIndex];
  188.  
  189. { fill in the runStart and runEnd fields from the style run array }
  190.         info.runStart := pTheRun^.first.runStart;
  191.         info.runEnd := pTheRun^.second.runStart;
  192.  
  193. { copy the style information from the appropriate entry in the style table }
  194.         info.runAttrs := pWE^.hStyles^^[pTheRun^.first.styleIndex].info;
  195.  
  196.     end;  { _WEGetIndStyle }
  197.  
  198.     procedure WEGetRunInfo (offset: LongInt;
  199.                                     var info: WERunInfo;
  200.                                     hWE: WEHandle);
  201.     begin
  202.         _WEGetIndStyle(_WEOffsetToRun(offset, hWE), info, hWE);
  203.     end;  { WEGetRunInfo }
  204.  
  205.     function WEGetSelectedObject (var hObjectDesc: WEObjectDescHandle;
  206.                                     hWE: WEHandle): OSErr;
  207.         var
  208.             pWE: WEPtr;
  209.             runInfo: WERunInfo;
  210.     begin
  211.  
  212. { assume current selection is not an embedded object }
  213.         WEGetSelectedObject := weObjectNotFoundErr;
  214.         hObjectDesc := nil;
  215.  
  216. { check selection range }
  217.         pWE := hWE^;
  218.         if (pWE^.selEnd - pWE^.selStart = 1) then
  219.             begin
  220.  
  221. { check run info }
  222.                 WEGetRunInfo(pWE^.selStart, runInfo, hWE);
  223.                 hObjectDesc := WEObjectDescHandle(runInfo.runAttrs.runStyle.tsObject);
  224.                 if (hObjectDesc <> nil) then
  225.                     WEGetSelectedObject := noErr;
  226.             end;
  227.     end;  { WEGetSelectedObject }
  228.  
  229.     function WEFindNextObject (offset: LongInt;
  230.                                     var hObjectDesc: WEObjectDescHandle;
  231.                                     hWE: WEHandle): LongInt;
  232.         var
  233.             pWE: WEPtr;
  234.             pStyles: StyleTablePtr;
  235.             pRun: RunArrayElementPtr;
  236.             obj: LongInt;
  237.     begin
  238.         pWE := hWE^;
  239.         WEFindNextObject := kInvalidOffset;
  240.         obj := kNullObject;
  241.  
  242. { do nothing if offset is already at the end of the text }
  243.         if (offset >= pWE^.textLength - 1) then
  244.             Exit(WEFindNextObject);
  245.  
  246. { get a pointer to the run array element immediately following offset }
  247.         pStyles := pWE^.hStyles^;
  248.         pRun := @pWE^.hRuns^^[_WEOffsetToRun(offset + 1, hWE)];
  249.  
  250. { perform a fast linear scan of the run array looking for a run whose }
  251. { corresponding style table entry points to an embedded object; }
  252. { the search will stop anyway because the last run array element has styleIndex = -1 }
  253.         while (pRun^.styleIndex >= 0) do
  254.             begin
  255.                 obj := pStyles^[pRun^.styleIndex].info.runStyle.tsObject;
  256.                 if (obj <> kNullObject) then
  257.                     begin
  258.                         WEFindNextObject := pRun^.runStart;
  259.                         Leave;    { enclosing while }
  260.                     end;
  261.                 pRun := RunArrayElementPtr(LongInt(pRun) + SizeOf(RunArrayElement));
  262.             end;  { while }
  263.         hObjectDesc := WEObjectDescHandle(obj);
  264.     end;  { WEFindNextObject }
  265.  
  266.     procedure _WEContinuousStyleRange (rangeStart, rangeEnd: LongInt;
  267.                                     var mode: Integer;
  268.                                     var ts: WETextStyle;
  269.                                     hWE: WEHandle);
  270.  
  271. { find out which style attributes are continous over the specified text range }
  272. { on entry, the mode bitmap specifies which attributes are to be checked }
  273. { on exit, the mode bitmap specifies the continuous attributes, also copied to ts }
  274.  
  275.         var
  276.             pWE: WEPtr;
  277.             bitmap: LongInt;
  278.             runIndex: LongInt;
  279.             runInfo: WERunInfo;
  280.     begin
  281.         pWE := hWE^;
  282.  
  283. { get bitmap of style attributes to check (masking out private and unused bits) }
  284.         bitmap := BAND(mode, weDoAll);
  285.  
  286. { get style info at the beginning of the specified range }
  287.         runIndex := _WEOffsetToRun(rangeStart, hWE);
  288.         _WEGetIndStyle(runIndex, runInfo, hWE);
  289.  
  290. { copy the specified fields to ts }
  291.         _WECopyStyle(runInfo.runAttrs.runStyle, ts, 0, BOR(bitmap, weDoReplaceFace));
  292.  
  293. { loop through style runs across the current selection range }
  294. { if we determine that all specified attributes are discontinuous, we exit prematurely }
  295.         repeat
  296.             _WEGetIndStyle(runIndex, runInfo, hWE);
  297.  
  298. { determine which attributes have changed, if any }
  299.             if BTST(bitmap, kModeFont) then
  300.                 if (runInfo.runAttrs.runStyle.tsFont <> ts.tsFont) then
  301.                     BCLR(bitmap, kModeFont);
  302.  
  303.             if BTST(bitmap, kModeFace) then
  304.                 if (runInfo.runAttrs.runStyle.tsFace <> ts.tsFace) then
  305.                     begin
  306.                         ts.tsFace := BAND(ts.tsFace, runInfo.runAttrs.runStyle.tsFace);
  307.                         if (ts.tsFace = 0) then
  308.                             BCLR(bitmap, kModeFace);
  309.                     end;
  310.  
  311.             if BTST(bitmap, kModeSize) then
  312.                 if (runInfo.runAttrs.runStyle.tsSize <> ts.tsSize) then
  313.                     BCLR(bitmap, kModeSize);
  314.  
  315.             if BTST(bitmap, kModeColor) then
  316.                 if (not _WEBlockCmp(@runInfo.runAttrs.runStyle.tsColor, @ts.tsColor, SizeOf(RGBColor))) then
  317.                     BCLR(bitmap, kModeColor);
  318.  
  319.             runIndex := runIndex + 1;
  320.         until (bitmap = 0) | (runInfo.runEnd >= rangeEnd);
  321.  
  322.         mode := bitmap;
  323.     end;  { _WEContinuousStyleRange }
  324.  
  325.     procedure _WESynchNullStyle (hWE: WEHandle);
  326.  
  327. { This routine fills the nullStyle field of the WE record with valid information }
  328. { and makes sure that the null style font belongs to the keyboard script. }
  329.  
  330.         var
  331.             pWE: WEPtr;
  332.             runIndex: LongInt;
  333.             keyboardScript: ScriptCode;
  334.             fontID: Integer;
  335.             runInfo: WERunInfo;
  336.     begin
  337.         pWE := hWE^;        { the WE record must be already locked }
  338.  
  339. { find the run index of the style run preceding the insertion point }
  340.         runIndex := _WEOffsetToRun(pWE^.selStart - 1, hWE);
  341.  
  342. { if the nullStyle record is marked as invalid, fill it with the style attributes }
  343. { associated with the character preceding the insertion point, and mark it as valid }
  344.         if (not BTST(pWE^.flags, weFUseNullStyle)) then
  345.             begin
  346.                 _WEGetIndStyle(runIndex, runInfo, hWE);
  347.                 pWE^.nullStyle := runInfo.runAttrs;
  348.                 BSET(pWE^.flags, weFUseNullStyle);
  349.             end;
  350.  
  351. { if only the Roman script is installed, we're finished }
  352.         if (not BTST(pWE^.flags, weFNonRoman)) then
  353.             Exit(_WESynchNullStyle);
  354.  
  355. { *** FONT / KEYBOARD SYNCHRONIZATION *** }
  356. { get the keyboard script }
  357.         keyboardScript := GetScriptManagerVariable(smKeyScript);
  358.  
  359. { find out what font will be used for the next character typed }
  360.         fontID := pWE^.nullStyle.runStyle.tsFont;
  361.  
  362. { do nothing if the font script is the same as the keyboard script }
  363.         if (FontToScript(fontID) = keyboardScript) then
  364.             Exit(_WESynchNullStyle);
  365.  
  366. { scan style runs starting from the insertion point backwards,}
  367. { looking for the first font belonging to the keyboard script }
  368.         repeat
  369.             _WEGetIndStyle(runIndex, runInfo, hWE);
  370.             fontID := runInfo.runAttrs.runStyle.tsFont;
  371.             if (FontToScript(fontID) = keyboardScript) then
  372.                 Leave;
  373.             runIndex := runIndex - 1;
  374.         until (runIndex < 0);
  375.  
  376. { if no font was ever used for the keyboard script, default to the }
  377. { application font for the script }
  378.         if (runIndex < 0) then
  379.             fontID := GetScriptVariable(keyboardScript, smScriptAppFond);
  380.  
  381. { change the font in the null style record }
  382.         pWE^.nullStyle.runStyle.tsFont := fontID;
  383.  
  384.     end;  { _WESynchNullStyle }
  385.  
  386.     function WEContinuousStyle (var mode: Integer;
  387.                                     var ts: WETextStyle;
  388.                                     hWE: WEHandle): Boolean;
  389.  
  390. { find out which style attributes are continous over the selection range }
  391. { on entry, the mode bitmap specifies which attributes are to be checked }
  392. { on exit, the mode bitmap specifies the continuous attributes, also copied to ts }
  393. { return TRUE if all specified attributes are continuous }
  394.  
  395.         var
  396.             pWE: WEPtr;
  397.             oldMode: Integer;
  398.             saveWELock: Boolean;
  399.     begin
  400.  
  401. { lock the WE record }
  402.         saveWELock := _WESetHandleLock(hWE, true);
  403.         pWE := hWE^;
  404.  
  405. { mask out private and unused bits in mode so we don't run the risk of overwriting }
  406. { memory past the ts record (which is defined as TextStyle in the public interfaces) }
  407.         mode := BAND(mode, weDoAll);
  408.  
  409. { two rather different paths are taken depending on whether }
  410. { the selection range is empty or not }
  411.         if (pWE^.selStart = pWE^.selEnd) then
  412.             begin
  413.  
  414. { if the selection range is empty, always return TRUE and set ts }
  415. { from the nullStyle record, after having validated it }
  416.                 WEContinuousStyle := true;
  417.                 _WESynchNullStyle(hWE);
  418.                 _WECopyStyle(pWE^.nullStyle.runStyle, ts, 0, BOR(mode, weDoReplaceFace));
  419.             end
  420.         else
  421.             begin
  422.  
  423. { otherwise get the continuous style attributes over the selection range }
  424.                 oldMode := mode;
  425.                 _WEContinuousStyleRange(pWE^.selStart, pWE^.selEnd, mode, ts, hWE);
  426.  
  427. { return TRUE if mode hasn't changed }
  428.                 WEContinuousStyle := (oldMode = mode);
  429.  
  430.             end;
  431.  
  432. { unlock the WE record }
  433.         if (_WESetHandleLock(hWE, saveWELock)) then
  434.             ;
  435.  
  436.     end;  { WEContinuousStyle }
  437.  
  438.     procedure _WESegmentLoop (firstLine, lastLine: LongInt;
  439.                                     function Callback (pLine: LinePtr;
  440.                                                                     pAttrs: WERunAttributesPtr;
  441.                                                                     pSegment: Ptr;
  442.                                                                     segmentStart, segmentLength: LongInt;
  443.                                                                     styleRunPosition: JustStyleCode): Boolean;
  444.                                     hWE: WEHandle);
  445.  
  446. { For each style segment on every line in the specified range, set up }
  447. { text attributes in the port and call the callback. }
  448. { the WE record must be already locked }
  449.  
  450.         var
  451.             pWE: WEPtr;
  452.             pLines: LineArrayPtr;
  453.             pText: LongInt;
  454.             lineIndex: LongInt;
  455.             runIndex, previousRunIndex: LongInt;
  456.             lineStart, lineEnd, segmentStart, segmentEnd: LongInt;
  457.             styleRunPosition: JustStyleCode;
  458.             runInfo: WERunInfo;
  459.             saveLineLock: Boolean;
  460.             saveTextLock: Boolean;
  461.             saveEnvironment: QDEnvironment;
  462.     begin
  463.         pWE := hWE^;
  464.  
  465. { save the QuickDraw environment }
  466.         _WESaveQDEnvironment(pWE^.port, BTST(pWE^.flags, weFHasColorQD), saveEnvironment);
  467.  
  468. { make sure firstLine and lastLine are within the allowed range }
  469.         lineIndex := pWE^.nLines - 1;
  470.         firstLine := _WEPinInRange(firstLine, 0, lineIndex);
  471.         lastLine := _WEPinInRange(lastLine, 0, lineIndex);
  472.  
  473. { lock the line array }
  474.         saveLineLock := _WESetHandleLock(pWE^.hLines, true);
  475.         pLines := pWE^.hLines^;
  476.  
  477. { lock the text }
  478.         saveTextLock := _WESetHandleLock(pWE^.hText, true);
  479.         pText := LongInt(pWE^.hText^);
  480.  
  481. { find the style run index corresponding to the beginning of the first line }
  482.         runIndex := _WEOffsetToRun(pLines^[firstLine].lineStart, hWE);
  483.         previousRunIndex := -1;
  484.  
  485. { loop thru the specified lines }
  486.         for lineIndex := firstLine to lastLine do
  487.             begin
  488.  
  489. { get line start and line end }
  490.                 lineStart := pLines^[lineIndex].lineStart;
  491.                 lineEnd := pLines^[lineIndex + 1].lineStart;
  492.  
  493. { loop thru each style run on this line }
  494.                 repeat
  495.  
  496. { get style run information for the current style run }
  497.                     _WEGetIndStyle(runIndex, runInfo, hWE);
  498.  
  499.                     if (previousRunIndex <> runIndex) then
  500.                         begin
  501.  
  502. { set new text attributes }
  503.                             TextFont(runInfo.runAttrs.runStyle.tsFont);
  504.                             __TextFace(runInfo.runAttrs.runStyle.tsFace);
  505.                             TextSize(runInfo.runAttrs.runStyle.tsSize);
  506.  
  507. { remember previous run index }
  508.                             previousRunIndex := runIndex;
  509.                         end;
  510.  
  511. { determine the relative position of this style run on the line }
  512.                     styleRunPosition := 0;                                        { onlyStyleRun }
  513.  
  514.                     if (runInfo.runStart <= lineStart) then
  515.                         segmentStart := lineStart
  516.                     else
  517.                         begin
  518.                             styleRunPosition := styleRunPosition + 2;    { rightStyleRun or middleStyleRun }
  519.                             segmentStart := runInfo.runStart;
  520.                         end;
  521.  
  522.                     if (runInfo.runEnd >= lineEnd) then
  523.                         segmentEnd := lineEnd
  524.                     else
  525.                         begin
  526.                             styleRunPosition := styleRunPosition + 1;    { leftStyleRun or middleStyleRun }
  527.                             segmentEnd := runInfo.runEnd;
  528.                         end;
  529.  
  530. {$IFC WASTE_DEBUG}
  531. { our callback should never see an embedded object if the segment is empty }
  532.                     _WEAssert((segmentStart < segmentEnd) or (runInfo.runAttrs.runStyle.tsObject = kNullObject), 'Embedded Object in Empty Segment');
  533. {$ENDC}
  534.  
  535. { do the callback }
  536.                     if Callback(@pLines^[lineIndex], @runInfo.runAttrs, Ptr(pText + segmentStart), segmentStart, segmentEnd - segmentStart, styleRunPosition) then
  537.                         Leave;
  538.  
  539. { advance style run index, unless this style run goes on to the next line }
  540.                     if (runInfo.runEnd <= lineEnd) then
  541.                         runIndex := runIndex + 1;
  542.  
  543.                 until (runInfo.runEnd >= lineEnd);
  544.  
  545.             end;  { for }
  546.  
  547. { unlock the text }
  548.         if (_WESetHandleLock(pWE^.hText, saveTextLock)) then
  549.             ;
  550.  
  551. { unlock the line array }
  552.         if (_WESetHandleLock(pWE^.hLines, saveLineLock)) then
  553.             ;
  554.  
  555. { restore the QuickDraw environment }
  556.         _WERestoreQDEnvironment(saveEnvironment);
  557.  
  558.     end;  { _WESegmentLoop }
  559.  
  560. {$IFC WASTE_SEGMENT}
  561. {$S WASTE_TSM_SUPPORT}
  562. {$ENDC}
  563.  
  564.     procedure _WEDrawTSMHilite (var segmentRect: Rect;
  565.                                     tsFlags: SignedByte);
  566.         var
  567.             flags: LongInt;
  568.             underlineHeight: Integer;
  569.             background, foreground, saveForeground: RGBColor;
  570.             isColorPort: Boolean;
  571.             usingTrueGray: Boolean;
  572.     begin
  573.         flags := tsFlags;
  574.         isColorPort := CGrafPtr(GetQDGlobalsPtr^.thePort)^.portVersion < 0;
  575.         usingTrueGray := false;
  576.  
  577. { by default, the pen pattern is solid }
  578.         PenPat(GetQDGlobalsPtr^.black);
  579.  
  580. { if we're drawing in color, set the foreground color }
  581.         if (isColorPort) then
  582.             begin
  583.  
  584. { save foreground color }
  585.                 GetForeColor(saveForeground);
  586.  
  587. { by default, the foreground color is black }
  588.                 foreground.red := 0;
  589.                 foreground.green := 0;
  590.                 foreground.blue := 0;
  591.  
  592. { if we're underlining raw (unconverted) text, see if a "true gray" is available }
  593.                 if (not BTST(flags, tsTSMConverted)) then
  594.                     begin
  595.                         GetBackColor(background);
  596.                         usingTrueGray := GetGray(GetGDevice, background, foreground);
  597.                     end;  { if raw text }
  598.  
  599. { set the foreground color }
  600.                 RGBForeColor(foreground);
  601.  
  602.             end;  { if color graf port }
  603.  
  604. { if we're underlining raw (unconverted) text and no true gray is available, }
  605. { simulate gray with a 50% pattern }
  606.         if (not BTST(flags, tsTSMConverted)) then
  607.             if (usingTrueGray = false) then
  608.                 PenPat(GetQDGlobalsPtr^.gray);
  609.  
  610. { use a 2-pixel tall underline if text is "selected", else use a 1-pixel tall underline }
  611.         if BTST(flags, tsTSMSelected) then
  612.             underlineHeight := 2
  613.         else
  614.             underlineHeight := 1;
  615.  
  616. { segmentRect becomes the rectangle to paint }
  617.         InsetRect(segmentRect, 1, 0);
  618.         segmentRect.top := segmentRect.bottom - underlineHeight;
  619.  
  620. { draw the underline }
  621.         PaintRect(segmentRect);
  622.  
  623. { restore the foreground color }
  624.         if (isColorPort) then
  625.             RGBForeColor(saveForeground);
  626.  
  627.     end;  { _WEDrawTSMHilite }
  628.  
  629. {$IFC WASTE_SEGMENT}
  630. {$S}
  631. {$ENDC}
  632.  
  633.     procedure _WEDrawLines (firstLine, lastLine: LongInt;
  634.                                     doErase: Boolean;
  635.                                     hWE: WEHandle);
  636.  
  637. { draw the specified range of lines }
  638. { we can safely assume that the WE record is already locked }
  639. { and the port is already set the pWE^.port }
  640.  
  641.         var
  642.             pWE: WEPtr;
  643.             lineRect: Rect;                            { rectangle enclosing the current line }
  644.             drawRect: Rect;                        { visible portion of the line rectangle }
  645.             bounds: Rect;                            { bounds of the offscreen buffer, in global coordinates }
  646.             screenPort: GrafPtr;
  647.             screenDevice: GDHandle;
  648.             offscreenPixels: PixMapHandle;
  649.             usingColor: Boolean;                { TRUE if we're drawing in color }
  650.             usingOffscreen: Boolean;            { TRUE if we're using an offscreen port }
  651.             drawingOffscreen: Boolean;        { TRUE if actually drawing to an offscreen buffer }
  652.  
  653.         function SLDraw (pLine: LinePtr;
  654.                                         pAttrs: WERunAttributesPtr;
  655.                                         pSegment: Ptr;
  656.                                         segmentStart, segmentLength: LongInt;
  657.                                         styleRunPosition: JustStyleCode): Boolean;
  658.             var
  659.                 slop: Fixed;
  660.                 segmentRect: Rect;
  661.                 theColorBlack: RGBColor;
  662.                 err: OSErr;
  663.         begin
  664.             SLDraw := false;                            { keep looping }
  665.  
  666. { is this the first segment on this line? }
  667.             if (styleRunPosition <= leftStyleRun) then
  668.                 begin
  669.  
  670. { calculate the line rectangle (the rectangle which completely encloses the current line) }
  671.                     lineRect.left := pWE^.destRect.left;
  672.                     lineRect.right := pWE^.destRect.right;
  673.                     lineRect.top := pWE^.destRect.top + pLine^.lineOrigin;
  674.                     lineRect.bottom := pWE^.destRect.top + LinePeek(pLine)^.second.lineOrigin;
  675.  
  676. { calculate the visible portion of this rectangle }
  677. { we do this by intersecting the line rectangle with the view rectangle }
  678.                     drawRect := pWE^.viewRgn^^.rgnBBox;
  679.                     if SectRect(lineRect, drawRect, drawRect) then
  680.                         ;
  681.  
  682.                     if (usingOffscreen) then
  683.                         begin
  684.  
  685. { calculate the boundary rectangle for the offscreen buffer }
  686. { this is simply drawRect converted to global coordinates }
  687.                             bounds := drawRect;
  688.                             LocalToGlobal(bounds.topLeft);
  689.                             LocalToGlobal(bounds.botRight);
  690.  
  691. { update the offscreen graphics world for the new bounds (this could fail) }
  692.                             drawingOffscreen := false;
  693.                             if (UpdateGWorld(GWorldPtr(pWE^.offscreenPort), 0, bounds, nil, nil, 0) >= 0) then
  694.                                 begin
  695.  
  696. { get the pixel map associated with the offscreen graphics world }
  697.  
  698. { NOTE: when running on a 68000 machine with the original QuickDraw, }
  699. { a GWorld is just an extended GrafPort, and GetGWorldPixMap actually }
  700. { returns a handle to a _copy_ of the GrafPort portBits (a BitMap, not a PixMap). }
  701. { An important side-effect of this is that when we call SetOrigin, }
  702. { only the original portBits is offset, not the copy. }
  703.  
  704.                                     offscreenPixels := GetGWorldPixMap(GWorldPtr(pWE^.offscreenPort));
  705.  
  706. { lock it down }
  707.                                     if LockPixels(offscreenPixels) then
  708.                                         begin
  709.  
  710. { offscreen pixel buffer allocation was successful }
  711.                                             drawingOffscreen := true;
  712.  
  713. { switch graphics world }
  714.                                             SetGWorld(GWorldPtr(pWE^.offscreenPort), nil);
  715.  
  716. { synchronize the coordinate system of the offscreen port with that of the screen port }
  717.                                             SetOrigin(drawRect.left, drawRect.top);
  718.  
  719. { reset the offscreen clip region }
  720.                                             ClipRect(drawRect);
  721.  
  722.                                         end;
  723.                                 end;  { if pixel buffer allocation was successful }
  724.                         end;  { if usingOffscreen }
  725.  
  726. { if doErase is TRUE, erase the drawable area before drawing text }
  727.                     if (doErase) then
  728.                         EraseRect(drawRect);
  729.  
  730. { position the pen }
  731.                     MoveTo(lineRect.left + _WECalcPenIndent(pLine^.lineSlop, pWE^.alignment), lineRect.top + pLine^.lineAscent);
  732.  
  733.                 end;  { if first segment on line }
  734.  
  735. { if drawingOffscreen, switch thePort to the offscreen port }
  736. { and synchronize text attributes }
  737.             if (drawingOffscreen) then
  738.                 begin
  739.                     SetPort(pWE^.offscreenPort);
  740.                     TextFont(pAttrs^.runStyle.tsFont);
  741.                     __TextFace(pAttrs^.runStyle.tsFace);
  742.                     TextSize(pAttrs^.runStyle.tsSize);
  743.                 end;  { if drawingOffscreen }
  744.  
  745. { get horizontal coordinate of the pen before drawing the segment }
  746.             GetPen(segmentRect.topLeft);
  747.  
  748. { set the foreground color }
  749.             if (usingColor) then
  750.                 RGBForeColor(pAttrs^.runStyle.tsColor);
  751.  
  752.             if (pAttrs^.runStyle.tsObject <> kNullObject) then
  753.                 begin
  754.  
  755. { EMBEDDED OBJECT }
  756.                     err := _WEDrawObject(WEObjectDescHandle(pAttrs^.runStyle.tsObject));
  757.  
  758.                 end
  759.             else
  760.                 begin
  761.  
  762. { REGULAR TEXT }
  763.  
  764.                     slop := 0;
  765.  
  766. { calculate the "slop" (extra space) for this text segment (justified text only) }
  767.                     if (pWE^.alignment = weJustify) then
  768.                         begin
  769.  
  770. { if this is the last segment on the line, strip trailing spaces }
  771.                             if (not Odd(styleRunPosition)) then
  772.                                 segmentLength := VisibleLength(pSegment, segmentLength);
  773.  
  774. { calculate how much extra space is to be applied to this text segment }
  775.                             slop := FixMul(PortionLine(pSegment, segmentLength, styleRunPosition, Point(kOneToOneScaling), Point(kOneToOneScaling)), pLine^.lineJustAmount);
  776.  
  777.                         end;  { if alignment = weJustify }
  778.  
  779. {$IFC WASTE_DEBUG}
  780.                     _WEAssert(pWE^.drawTextHook <> nil, 'Missing DrawText Hook');
  781. {$ENDC}
  782.  
  783. { draw the segment }
  784.                     CallWEDrawTextProc(pSegment, segmentLength, slop, styleRunPosition, hWE, pWE^.drawTextHook);
  785.  
  786.                 end;
  787.  
  788. { get horizontal coordinate of the pen after drawing the segment }
  789.             GetPen(segmentRect.botRight);
  790.             segmentRect.bottom := lineRect.bottom;
  791.  
  792. { if this segment is in the TSM area, underline it in the appropriate way }
  793.             if BTST(pAttrs^.runStyle.tsFlags, tsTSMHilite) then
  794.                 _WEDrawTSMHilite(segmentRect, pAttrs^.runStyle.tsFlags);
  795.  
  796.             if (drawingOffscreen) then
  797.                 begin
  798.                     if (not Odd(styleRunPosition)) then
  799.                         begin
  800.  
  801. { after drawing offscreen the last segment, }
  802. { prepare to copy the offscreen buffer to video RAM }
  803.  
  804. { first set the graphics world to the screen port }
  805.                             SetGWorld(GWorldPtr(screenPort), screenDevice);
  806.  
  807. { before calling CopyBits, set the foreground color to black to avoid colorization (color only) }
  808.                             if (usingColor) then
  809.                                 begin
  810.                                     theColorBlack.red := 0;
  811.                                     theColorBlack.green := 0;
  812.                                     theColorBlack.blue := 0;
  813.                                     RGBForeColor(theColorBlack);
  814.                                 end;
  815.  
  816. { copy the offscreen image of the [visible portion of the] line to the screen }
  817.                             CopyBits(pWE^.offscreenPort^.portBits, screenPort^.portBits, drawRect, drawRect, srcCopy, nil);
  818.  
  819. { restore the original offscreen coordinate system and unlock the pixel image }
  820.                             SetPort(pWE^.offscreenPort);
  821.                             SetOrigin(0, 0);
  822.                             if (usingColor) then
  823.                                 RGBForeColor(theColorBlack);            { this fixes a bug in Style 1.3 }
  824.                             UnlockPixels(offscreenPixels);
  825.  
  826.                         end;  { if last segment }
  827.  
  828. { restore the screen port for _WESegmentLoop }
  829.                     SetPort(screenPort);
  830.  
  831.                 end;  { if drawingOffscreen }
  832.         end;  { SLDraw }
  833.  
  834.     begin  { _WEDrawLines }
  835.         pWE := hWE^;
  836.         usingOffscreen := false;
  837.         drawingOffscreen := false;
  838.  
  839. { do nothing if our graphics port is not visible }
  840.         if EmptyRgn(pWE^.port^.visRgn) then
  841.             Exit(_WEDrawLines);
  842.  
  843. { save graphics world }
  844.         GetGWorld(GWorldPtr(screenPort), screenDevice);
  845.  
  846. { If doErase is TRUE, we're drawing over old text, so we must erase each line }
  847. { before redrawing it.  But if the weFDrawOffscreen feature is enabled, we draw }
  848. { the entire line offscreen and then we copy the image right over the old line, }
  849. { without erasing it, thus achieving a very smooth drawing effect. }
  850.  
  851.         if ((doErase) and BTST(pWE^.flags, weFDrawOffscreen)) then
  852.             begin
  853.  
  854. { has an offscreen world already been allocated? }
  855.                 if (pWE^.offscreenPort = nil) then
  856.                     begin
  857.  
  858. { nope, then create one; its bounds are set initially to an arbitrary rectangle }
  859.                         SetRect(bounds, 0, 0, 1, 1);
  860.                         if (NewGWorld(GWorldPtr(pWE^.offscreenPort), 0, bounds, nil, nil, pixPurge + noNewDevice + useTempMem) <> noErr) then
  861.                             ;
  862.                     end;
  863.  
  864.                 usingOffscreen := (pWE^.offscreenPort <> nil);
  865.             end;
  866.  
  867.         usingColor := BTST(pWE^.flags, weFHasColorQD);
  868.         _WESegmentLoop(firstLine, lastLine, SLDraw, hWE);
  869.  
  870. { restore graphics world }
  871.         SetGWorld(GWorldPtr(screenPort), screenDevice);
  872.  
  873.     end;  { _WEDrawLines }
  874.  
  875.     function _WECalcPenIndent (slop: Integer;
  876.                                     alignment: Integer): Integer;
  877.     begin
  878.  
  879. { if alignment is weFlushDefault, use the system global SysDirection }
  880.         if (alignment = weFlushDefault) then
  881.             if (GetSysDirection = 0) then
  882.                 alignment := weFlushLeft
  883.             else
  884.                 alignment := weFlushRight;
  885.  
  886.         if (alignment = weFlushRight) then
  887.             _WECalcPenIndent := slop                                { right aligned }
  888.         else if (alignment = weCenter) then
  889.             _WECalcPenIndent := slop div 2                        { centered }
  890.         else
  891.             _WECalcPenIndent := 0;                                    { left aligned or justified }
  892.     end;  { _WECalcPenIndent }
  893.  
  894.     procedure _WESaveQDEnvironment (port: GrafPtr;
  895.                                     saveColor: Boolean;
  896.                                     var theEnvironment: QDEnvironment);
  897.     begin
  898.         with theEnvironment do
  899.             begin
  900.                 GetPort(envPort);
  901.                 SetPort(port);
  902.                 GetPenState(envPen);
  903.                 PenNormal;
  904.                 envStyle.tsFont := port^.txFont;
  905.                 envStyle.tsFace := GrafPtr1(port)^.txFace;
  906.                 Boolean(envStyle.tsFlags) := saveColor;        { remember if color was saved }
  907.                 envStyle.tsSize := port^.txSize;
  908.                 if (saveColor) then
  909.                     GetForeColor(envStyle.tsColor);
  910.                 envMode := port^.txMode;
  911.                 TextMode(srcOr);
  912.             end;  { with }
  913.     end;  { _WESaveQDEnvironment }
  914.  
  915.     procedure _WERestoreQDEnvironment (var theEnvironment: QDEnvironment);
  916.     begin
  917.         with theEnvironment do
  918.             begin
  919.                 SetPenState(envPen);
  920.                 TextFont(envStyle.tsFont);
  921.                 __TextFace(envStyle.tsFace);
  922.                 TextSize(envStyle.tsSize);
  923.                 TextMode(envMode);
  924.                 if Boolean(envStyle.tsFlags) then
  925.                     RGBForeColor(envStyle.tsColor);
  926.                 SetPort(envPort);
  927.             end;  { with }
  928.     end;  { _WERestoreQDEnvironment }
  929.  
  930.     procedure _WEFillFontInfo (port: GrafPtr;
  931.                                     var targetStyle: WERunAttributes);
  932.  
  933. { given a WERunAttributes record, fill in the runHeight, runAscent fields etc. }
  934.         var
  935.             fInfo: FontInfo;
  936.             saveEnvironment: QDEnvironment;
  937.     begin
  938.         _WESaveQDEnvironment(port, false, saveEnvironment);
  939.         with targetStyle do
  940.             begin
  941.  
  942. { we don't want a zero font size; although QuickDraw accepts zero to mean }
  943. { the default font size, it can cause trouble to us when we do calculations }
  944.                 if (runStyle.tsSize = 0) then
  945.                     runStyle.tsSize := 12;
  946.  
  947. { set the text attributes }
  948.                 TextFont(runStyle.tsFont);
  949.                 TextSize(runStyle.tsSize);
  950.                 __TextFace(runStyle.tsFace);
  951.                 GetFontInfo(fInfo);
  952.                 runHeight := fInfo.ascent + fInfo.descent + fInfo.leading;
  953.                 runAscent := fInfo.ascent;
  954.             end;  { with }
  955.         _WERestoreQDEnvironment(saveEnvironment);
  956.     end;  { _WEFillFontInfo }
  957.  
  958.     procedure _WECopyStyle (var sourceStyle, targetStyle: WETextStyle;
  959.                                     offStyles: Integer;
  960.                                     mode: Integer);
  961.  
  962. { Copy some or all of the attributes composing sourceStyle to targetStyle. }
  963. { The mode parameter determines which attributes are to be copied and how. }
  964. { If mode contains weDoToggleFace, then offStyles indicates which }
  965. { QuickDraw styles are to be turned off. }
  966.  
  967.         var
  968.             longMode: LongInt;
  969.             longSize: LongInt;
  970.             sourceFace, targetFace: LongInt;
  971.  
  972.     begin
  973.         longMode := mode;    { this allows my compiler to generate tighter code }
  974.  
  975. { if the kModeFont bit is set, copy the font family number }
  976.         if BTST(longMode, kModeFont) then
  977.             begin
  978.                 targetStyle.tsFont := sourceStyle.tsFont;
  979.  
  980. {$IFC WASTE_RESOLVE_FONT_DESIGNATORS}
  981.                 if (targetStyle.tsFont = systemFont) then
  982.                     targetStyle.tsFont := GetSysFont;
  983.                 if (targetStyle.tsFont = applFont) then
  984.                     targetStyle.tsFont := GetAppFont;
  985. {$ENDC}
  986.             end;
  987.  
  988. { if the kModeSize or the kModeAddSize bit is set, alter the font size }
  989.         if (BAND(longMode, weDoSize + weDoAddSize) <> 0) then
  990.             begin
  991.  
  992. { copy size to a long variable to avoid integer overflows when doing additions }
  993.                 longSize := sourceStyle.tsSize;
  994.  
  995. { zero really means 12 }
  996.                 if (longSize = 0) then
  997.                     longSize := 12;
  998.  
  999. { if kModeAddSize is set, the source size is added to the target size, }
  1000. { otherwise the source size replaces the target size outright }
  1001.                 if BTST(longMode, kModeAddSize) then
  1002.                     longSize := longSize + targetStyle.tsSize;
  1003.  
  1004. { range-check the resulting size }
  1005.                 longSize := _WEPinInRange(longSize, kMinFontSize, kMaxFontSize);
  1006.                 targetStyle.tsSize := longSize;
  1007.  
  1008.             end;  { if alter size }
  1009.  
  1010. { if kModeFace is set, copy the QuickDraw styles (tsFace field); }
  1011. { the (rather complex) rules for copying the styles are explained below in detail }
  1012.         if BTST(longMode, kModeFace) then
  1013.             begin
  1014.                 sourceFace := sourceStyle.tsFace;
  1015.                 targetFace := targetStyle.tsFace;
  1016.  
  1017. { sourceFace replaces targetFace outright if one or both of these conditions hold: }
  1018. { 1. sourceFace is zero (= empty set = plain text) }
  1019. { 2. the kModeReplaceFace bit is set }
  1020.  
  1021.                 if ((sourceFace = 0) or BTST(longMode, kModeReplaceFace)) then
  1022.                     targetFace := sourceFace
  1023.                 else
  1024.                     begin
  1025.  
  1026. { Otherwise sourceFace is interpreted as a bitmap indicating }
  1027. { which styles are to be altered -- all other styles are left intact. }
  1028. { What exactly happens to the styles indicated in sourceFace }
  1029. { depends on whether the kModeToggleFace bit is set or clear. }
  1030.  
  1031. { if kModeToggleFace is set, turn a style off if it's set in offStyles, else turn it on }
  1032.                         if BTST(longMode, kModeToggleFace) then
  1033.                             targetFace := BOR(BXOR(sourceFace, offStyles), BAND(targetFace, BitNot(sourceFace)))
  1034.                         else
  1035.  
  1036. { if kModeToggleFace is clear, turn on the styles specified in sourceStyle }
  1037.                             targetFace := BOR(targetFace, sourceFace);
  1038.  
  1039. { the condense and extend attributes are mutually exclusive: if one is set }
  1040. { in sourceFace, remove it from targetFace }
  1041.                         if BTST(sourceFace, tsCondense) then
  1042.                             BCLR(targetFace, tsExtend)
  1043.                         else if BTST(sourceFace, tsExtend) then
  1044.                             BCLR(targetFace, tsCondense)
  1045.                     end;
  1046.  
  1047.                 targetStyle.tsFace := targetFace;
  1048.             end;  { if alter face }
  1049.  
  1050. { if kModeColor is set, change target color }
  1051.         if BTST(longMode, kModeColor) then
  1052.             targetStyle.tsColor := sourceStyle.tsColor;
  1053.  
  1054. { if kModeObject is set, copy object descriptor }
  1055.         if BTST(longMode, kModeObject) then
  1056.             targetStyle.tsObject := sourceStyle.tsObject;
  1057.  
  1058. { always clear targetStyle.tsFlags by default }
  1059.         targetStyle.tsFlags := 0;
  1060.  
  1061. { if kModeFlags is set, copy the tsFlags field }
  1062.         if BTST(longMode, kModeFlags) then
  1063.             targetStyle.tsFlags := sourceStyle.tsFlags;
  1064.  
  1065.     end;  { _WECopyStyle }
  1066.  
  1067.     function _WEOffsetInRange (offset: LongInt;
  1068.                                     edge: SignedByte;
  1069.                                     rangeStart, rangeEnd: LongInt): Boolean;
  1070.  
  1071. { return TRUE if the position specified by the pair < offset, edge > }
  1072. { is within the specified range }
  1073.  
  1074.     begin
  1075.  
  1076. { if edge is kTrailingEdge, offset really refers to the preceding character }
  1077.         if (edge = kTrailingEdge) then
  1078.             offset := offset - 1;
  1079.  
  1080. { return TRUE iff offset is within the specified range }
  1081.         _WEOffsetInRange := ((offset >= rangeStart) and (offset < rangeEnd));
  1082.  
  1083.     end;  { _WEOffsetInRange }
  1084.  
  1085. end.